package com.jacksay.vikingfl.autocompleter
{
	import com.jacksay.vikingfl.textinput.PromptTextInput;
	import com.jacksay.vikingfl.textinput.PromptTextInputEvent;
	import com.jacksay.vikingfl.utils.AutoCompleteStringUtils;
	
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.geom.Point;
	import flash.text.FontStyle;
	import flash.ui.Keyboard;
	
	import mx.collections.ArrayCollection;
	import mx.collections.ICollectionView;
	import mx.collections.Sort;
	import mx.collections.SortField;
	import mx.controls.List;
	import mx.core.ClassFactory;
	import mx.core.IFactory;
	import mx.events.FlexEvent;
	import mx.events.ListEvent;
	import mx.managers.PopUpManager;
	
	/**
	 * Dispatché lorsque l'item selectionné change
	 */
	[Event(name="selectedItemChange", type="com.jacksay.vikingfl.autocompleter.AutoCompleterRemoteEvent")]
	
	/**
	 * Autocompleter constitué d'un champ de saisie. Dans sa forme initiale, il 
	 * fonctionne comme une ComboBox traditionnelle. Il est conçu pour être 
	 * surchargé en vue d'une completion "connectée".
	 */
	public class AutoCompleterRemote extends PromptTextInput
	{
		/**
		 * Utilisé pour éviter de perdre le selectedItem lors du textChange
		 * 
		 * @author jacksay14
		 */
		public var fSelectedItemInProgress:Boolean = false;
		
		
		// ---------------------------------------------------------------------
		//
		// PUBLIC PROPERTIES
		//
		// ---------------------------------------------------------------------
	
		// ------------------------------------------------------------------
		// colorDropDown
		// ------------------------------------------------------------------
		/**
		 * Couleur des éléments dans la DropDown
		 *
		 * @default #00000
		 */
		[Inspectable(category="Other", defaultValue="#000000",type="String")]
		public function get colorDropDown() :String {
			return _colorDropDown;
		}
		
		/**
		 * @private
		 */
		private var _colorDropDown:String = "#000000";
		
		/**
		 * @private
		 */
		public function set colorDropDown( value:String ) :void {
			_colorDropDown = value;
		}
	
		// ------------------------------------------------------------------
		// completionVisible
		// ------------------------------------------------------------------
		/**
		 * Permet de savoir si la "completion" est affichée ou non.
		 *
		 * @default false
		 * @author Julien, Jacksay14
		 * 
		 */
		[Inspectable(category="Other", defaultValue=false,type="Boolean")]
		public function get completionVisible() :Boolean {
			if( completion == null ) return false;
			else return completion.visible;
		}
		
		// ------------------------------------------------------------------
		// labelField
		// ------------------------------------------------------------------
		/**
		 * Méthode de filtrage des données
		 *
		 * @default contain
		 * @see AutoCompleterRemoteMode
		 */
		[Inspectable(category="Other", defaultValue="",type="String")]
		public function get labelField() :String {
			return _labelField;
		}
		
		/**
		 * @private
		 */
		private var _labelField:String;
		
		/**
		 * @private
		 */
		public function set labelField( value:String ) :void {
			if( value != _labelField ){
				_labelField = value;
				sortField.name = value;
				dispatchEvent(new Event("sortChange"));
			}
		}
		
		
		
		// ------------------------------------------------------------------
		// completionLabelFunction
		// ------------------------------------------------------------------
		/**
		 * Méthode de rendu pour la completion
		 *
		 * @default null
		 */
		[Inspectable(category="Other", defaultValue=null,type="Function")]
		public function get completionLabelFunction() :Function {
			return _completionLabelFunction;
		}
		
		/**
		 * @private
		 */
		private var _completionLabelFunction:Function;
		
		/**
		 * @private
		 */
		public function set completionLabelFunction( value:Function ) :void {
			_completionLabelFunction = value;
		}
		
		
		
		// ------------------------------------------------------------------
		// selectedItemLabelFunction
		// ------------------------------------------------------------------
		/**
		 * Méthode de rendu pour les items SELECTIONNES
		 *
		 * @default null
		 */
		[Inspectable(category="Other", defaultValue=null,type="Function")]
		public function get selectedItemLabelFunction() :Function {
			return _selectedItemLabelFunction;
		}
		
		/**
		 * @private
		 */
		private var _selectedItemLabelFunction:Function;
		
		/**
		 * @private
		 */
		public function set selectedItemLabelFunction( value:Function ) :void {
			_selectedItemLabelFunction = value;
		}
		
		
		
		// ------------------------------------------------------------------
		// dropDownItemRenderer
		// ------------------------------------------------------------------
		/**
		 * Item renderer de rendu pour la completion
		 *
		 * @default contain
		 * @see AutoCompleterRemoteMode
		 */
		[Inspectable(category="Other", defaultValue="",type="IFactory")]
		public function get dropDownItemRenderer() :IFactory {
			return _dropDownItemRenderer;
		}
		
		/**
		 * @private
		 */
		private var _dropDownItemRenderer:IFactory;
		
		/**
		 * @private
		 */
		public function set dropDownItemRenderer( value:IFactory ) :void {
			_dropDownItemRenderer = value;
		}
		
		
		
		// ------------------------------------------------------------------
		// sortField
		// ------------------------------------------------------------------
		/**
		 * SortField appliqué au ICollectionView de la completion pour les 
		 * tries. Les propriétés <code>propertyName</code>
		 */
		[Inspectable(category="Other", type="SortField")]
		public function get sortField() :SortField {
			return _sortField;
		}
		
		/**
		 * @private
		 */
		private var _sortField:SortField = new SortField(null, true);
		
		/**
		 * @private
		 */
		public function set sortField( value:SortField ) :void {
			_sortField = value;
		}
		
		
		
		// ------------------------------------------------------------------
		// searchMode
		// ------------------------------------------------------------------
		/**
		 * Méthode de filtrage des données :
		 *  - contain : Contiend la chaine
		 *  - begin_by : Commence par
		 *
		 * @default contain
		 * @see AutoCompleterRemoteMode
		 */
		[Inspectable(category="Other", defaultValue="contain", enumeration="contain,beginBy,word",type="String")]
		public function get searchMode() :String {
			return _searchMode;
		}
		
		/**
		 * @private
		 */
		private var _searchMode:String = "contain";
		
		/**
		 * @private
		 */
		public function set searchMode( value:String ) :void {
			_searchMode = value;
		}
		
		
		
		// ------------------------------------------------------------------
		// searchSensitiveCase
		// ------------------------------------------------------------------
		/**
		 * Définit si la méthode de filtrage des données est sensible à la case,
		 * la donnée est placée dans sortField
		 *
		 * @default false
		 */
		[Bindable("sortChange")]
		[Inspectable(category="Other", defaultValue=contain, enumeration="true,false",type="Boolean")]
		public function get searchSensitiveCase() :Boolean {
			return !sortField.caseInsensitive;
		}
		
		/**
		 * @private
		 */
		public function set searchSensitiveCase( value:Boolean ) :void {
			if( value != !sortField.caseInsensitive ){
				sortField.caseInsensitive = !value;
				var sort:Sort = new Sort();
				sort.fields = [sortField];
				ICollectionView(dataProvider).sort = sort;
				dispatchEvent(new Event("sortChange"));
			}
		}
		
		// ------------------------------------------------------------------
		// searchSortByProperty
		// ------------------------------------------------------------------
		/**
		 * Définit le champ utilisé pour le trie de la completion.
		 *
		 * @default false
		 */
		[Bindable("sortChange")]
		[Inspectable(category="Other", defaultValue=null, type="String")]
		public function get searchSortByProperty() :String {
			return sortField.name;
		}
		
		/**
		 * @private
		 */
		public function set searchSortByProperty( value:String ) :void {
			if( value != sortField.name ){
				sortField.name = value;
				var sort:Sort = new Sort();
				sort.fields = [sortField];
				ICollectionView(dataProvider).sort = sort;
				dispatchEvent(new Event("sortChange"));
			}
		}


		
		// ------------------------------------------------------------------
		// remoteCallStartAt
		// ------------------------------------------------------------------
		/**
		 * Nombre de caractère minimum pour éxécuter la fonction de récupération
		 * des listes de donnée. N'est utilisé que dans le cadre du mode 
		 * connecté.
		 *
		 * @default 1
		 */
		[Inspectable(category="Other", defaultValue=1, type="int")]
		public function get remoteCallStartAt() :int {
			return _remoteCallStartAt;
		}
		
		/**
		 * @private
		 */
		private var _remoteCallStartAt:int = 1;
		
		/**
		 * @private
		 */
		public function set remoteCallStartAt( value:int ) :void {
			_remoteCallStartAt = value;
		}
		
		
		
		// ------------------------------------------------------------------
		// text
		// ------------------------------------------------------------------
		/**
		 * Texte affiché dans le champ
		 *
		 * @default 1
		 */
		[Inspectable(category="Other", defaultValue=1, type="int")]
		override public function get text():String {
			return super.text;
		}
		
		/**
		 * @private
		 */
		override public function set text( value:String ) :void {
			if( value != super.text ) {
				if( selectedItem != null && selectedLabel != value ){
					selectedItem = null;
				}
				super.text = value;
			}
		}
		
		
		
		// ------------------------------------------------------------------
		// selectedID
		// ------------------------------------------------------------------
		/**
		 * Permet de manipuler / obtenir les items via leurs clef primaires. 
		 * quand aucun item référencé dans la completion n'est selectionné, la
		 * valeur retournée est <code>-1</code>. Les ID à 0 n'étant pas d'usage
		 * dans le bases de données, les valeurs < 1 sont considérées comme 
		 * égales à -1.
		 * Pour vider le champ de saisi, vous devez utiliser 
		 * <code>selectedID=-1</code>
		 *
		 * @default -1
		 */
		[Inspectable(category="Other", defaultValue=-1, type="int")]
		[Bindable("selectedItemChange")]
		public function get selectedID() :int {
			if( _selectedID == 0 ) {
				_selectedID = -1;
			}
			return _selectedID;
		}
		
		/**
		 * @private
		 */
		private var _selectedID:int = -1;
		
		/**
		 * @private
		 */
		public function set selectedID( value:int ) :void {
			// Si la valeur affecté == -1 on purge
			if( value == -1 || value == 0 ) {
				text = "";
			} 
			
			if( selectedItem == null || selectedItem[idName] != value ){
				_selectedID = value;
				if( value < 1 ){
					selectedItem = null;
					return;
				}
				
				var dt:Array = (dataProvider as ArrayCollection).source;
				
				// On recherche l'id dans les items de la completion
				if( dt != null ){
					var i:int;
					var lng:int = dt.length;
					for ( i=0; i<lng; i++ ){
						if( dt[i][idName] == value ){
							selectedItem = dt[i];
							text = selectedItemLabelFunction(dt[i]);
							return;
						}
					}
				}
				
				// L'item n'a pas été trouvé dans le model
				if( fRealTimeRequest ) remoteGetItemById(value);
				else selectedItem = null;
			}
		}
		
		
		
		// ------------------------------------------------------------------
		// idName
		// ------------------------------------------------------------------
		/**
		 * Nom de la propriété servant de clef primaire. De préférence un type
		 * int unique.
		 *
		 * @default "id"
		 */
		[Inspectable(category="Other", defaultValue="id", type="String")]
		public function get idName() :String {
			return _idName;
		}
		
		/**
		 * @private
		 */
		private var _idName:String = "id";
		
		/**
		 * @private
		 */
		public function set idName( value:String ) :void {
			_idName = value;
		}
		
		
		
		// ------------------------------------------------------------------
		// useRemoteFunction
		// ------------------------------------------------------------------
		/**
		 * Détermine si l'autocompleter est en mode connecté. Pour mettre en 
		 * place un AutoCompleter 'connecté', vous devez surcharger la classe 
		 * AutoCompleterRemote en réécrivant les méthodes : 
		 *  - <code>remoteGetItemById( value:int ) :void</code>
		 *  - <code>remoteGetItemList() :void</code>
		 */
		protected var fRealTimeRequest:Boolean = false;
		
		
		
		
		
		// ------------------------------------------------------------------
		// completionMaxRow
		// ------------------------------------------------------------------
		/**
		 * Nombre de ligne affichées dans la completion.
		 *
		 * @default 5
		 */
		[Inspectable(category="Other", defaultValue=1, type="int")]
		public function get completionMaxRow() :int {
			return _completionMaxRow;
		}
		
		/**
		 * @private
		 */
		private var _completionMaxRow:int = 5;
		
		/**
		 * @private
		 */
		public function set completionMaxRow( value:int ) :void {
			_completionMaxRow = value;
		}
		
		
		
		// ------------------------------------------------------------------
		// selectedItem
		// ------------------------------------------------------------------
		/**
		 * Item selectionné dans la completion.
		 *
		 * @default null
		 */
		[Bindable("selectedItemChange")]
		[Inspectable(category="Other", defaultValue=null, type="*")]
		public function get selectedItem() :* {
			return _selectedItem;
		}
		
		/**
		 * @private
		 */
		private var _selectedItem:* = null;
		
		/**
		 * @private
		 */
		public function set selectedItem( value:* ) :void {
			if( value != _selectedItem )
			{
				fSelectedItemInProgress = true;
				var arrCompletion:Array = ArrayCollection(dataProvider).source;
				
				if( value != null ){
					
					var i:int;
					var lng:int = arrCompletion.length;
					var item:*;
					
					for( i=0; i<lng; i++ ){
						item = arrCompletion[i];
						if( item == value ){
							_selectedItem = item;
							_selectedID = item[idName];
							text = selectedLabel;
							setSelection(0, text.length);
							invalidateStyle();
							fSelectedItemInProgress = false;
							dispatchEvent(new AutoCompleterRemoteEvent(AutoCompleterRemoteEvent.SELECT_ITEM_CHANGE));
							return;
						} 
					}
				}
				
				_selectedItem = null;
				_selectedID = -1;
				invalidateStyle();
				fSelectedItemInProgress = false;
				dispatchEvent(new AutoCompleterRemoteEvent(AutoCompleterRemoteEvent.SELECT_ITEM_CHANGE));
			}
		}
		
		
		
		/** 
		 * Retourne le selectedItem formaté pour l'affichage dans le champ de 
		 * texte. Si aucun item n'est selectionné, retourne une chaine vide.
		 * 
		 * @return String
		 * @author jacksay14
		 */
		[Bindable("selectedItemChange")]
		public function get selectedLabel() :String {
			if( selectedItem == null ){
				return "";
			} 
			else if( selectedItemLabelFunction == null ){
				return _selectedItem.toString();
			} 
			else {
				return selectedItemLabelFunction(selectedItem);
			}
		} 
		
		
		
		// ------------------------------------------------------------------
		// completionLabelFunctionRenderer
		// ------------------------------------------------------------------
		/**
		 * Mise en forme de la completion
		 *
		 * @default null
		 */
		[Inspectable(category="Other", defaultValue=null, type="Function")]
		public function get completionLabelFunctionRenderer() :Function {
			return _completionLabelFunctionRenderer;
		}
		
		/**
		 * @private
		 */
		private var _completionLabelFunctionRenderer:Function;
		
		/**
		 * @private
		 */
		public function set completionLabelFunctionRenderer( value:Function ) :void {
			_completionLabelFunctionRenderer = value;
		}
		
		
		
		// ------------------------------------------------------------------
		// filterFunction
		// ------------------------------------------------------------------
		/**
		 * Fonction de filtrage des données dans la completion. signature : 
		 * <code>filterFunction( o:* ) :Boolean</code>
		 *
		 * @default null
		 */
		[Inspectable(category="Other", defaultValue=null, type="Function")]
		public function get filterFunction() :Function {
			return _filterFunction;
		}
		
		private var _filterFunction:Function = null;
		
		/**
		 * @private
		 */
		public function set filterFunction( value:Function ) :void {
			_filterFunction = value;
		}
		
		

		// ------------------------------------------------------------------
		// dataProvider
		// ------------------------------------------------------------------
		/**
		 * Mise en forme de la selection dans le champ de texte
		 *
		 * @author jacksay14
		 * @default null
		 */
		[Inspectable(category="Other", defaultValue=null, type="Object")]
		public function get dataProvider() :Object {
			if( completion == null ) return null;
			return completion.dataProvider;
		}
		
		/**
		 * @private
		 */
		public function set dataProvider( value:Object ) :void {
			if( completion == null ) return;
			
			completion.dataProvider = value;
			
			ICollectionView(dataProvider).filterFunction = filterFunction;
			if( sortField.name != null ){
				var sort:Sort = new Sort();
				sort.fields = [sortField];
				ICollectionView(dataProvider).sort = sort;
			} else {
				ICollectionView(dataProvider).sort = null;
			}
		}
		
		
		
		// ------------------------------------------------------------------
		// hasSelectedItem
		// ------------------------------------------------------------------
		/**
		 * Permet de savoir si un item est selectionné ou pas
		 * 
		 * @author jacksay14
		 */
		[Bindable("selectedItemChange")]
		public function get hasSelectedItem() :Boolean {
			return selectedItem != null;
		}
		
		
		
		// ---------------------------------------------------------------------
		//
		// PROTECTED PROPERTIES
		//
		// ---------------------------------------------------------------------
		protected var completion:List = new List();
		
		
		
		// ---------------------------------------------------------------------
		//
		// CONSTRUCTEUR
		//
		// ---------------------------------------------------------------------
		public function AutoCompleterRemote()
		{
			super();
			if( dataProvider == null ) dataProvider = new ArrayCollection();

			if (completionLabelFunction == null) {			
				completionLabelFunction = defaultCompletionLabelFunction;
			}
			
			if ( selectedItemLabelFunction == null ) {
				selectedItemLabelFunction = defaultSelectedItemLabelFunction;
			}
			
			if (_filterFunction == null) {
				filterFunction = defaultFilterFunction;
			}
		}
		
		// ---------------------------------------------------------------------
		//
		// PRIVATE PROPERTIES (FLAGS)
		//
		// ---------------------------------------------------------------------
		private var fRemoteInProgress:Boolean = false;
		private var fCompostionPosition:Boolean = false;
		
		
		
		// ---------------------------------------------------------------------
		//
		// HANDLER
		//
		// ---------------------------------------------------------------------
		

		override protected function initializationComplete():void {
			super.initializationComplete();
			addEventListener(KeyboardEvent.KEY_UP, handlerKeyUp, false, 0, true);
			addEventListener(PromptTextInputEvent.TEXT_CHANGE, handlerTextChange, false, 0, true);
		}
		
		/**
		 * FocusEvent.FOCUS_OUT : A la perte de focus du composant
		 * 
		 * @see hideCompletion
		 * @param e FocusEvent
		 * @author jacksay14
		 */
		override protected function handlerFocusOut( e:FocusEvent ) :void {
			super.handlerFocusOut(e);
			hideCompletion();
		}
		
		
		
		/**
		 * completion > ListEvent.ITEM_CLICK : Quand un item dans la liste est 
		 * selectionné via un click de la souris.
		 * 
		 * @param e ListeEvent
		 * @author jacksay14
		 */
		protected function handlerItemClick( e:ListEvent ) :void {
			completionSelect();
		}
		
		
		
		/**
		 * PromptTextInputEvent.TEXT_CHANGE : Suriviens quand un changement 
		 * EFFECTIF du texte est détecté.
		 * 
		 * @param e PromptTextInputEvent
		 * @author jacksay14
		 */
		protected function handlerTextChange( e:PromptTextInputEvent ) :void {
			super.handlerTextInput(e);
			
			
			// Si un selected item est en cours d'affectation
			// on ne fait rien pour éviter le reset du selectedItem
			if( fSelectedItemInProgress ) return;
			
			// On purge le selectedItem si besoin
			if( selectedItem != null )
			{
				if( selectedLabel != text ){
					selectedItem = null;
				}
			}
			
			// On test si le composant est en mode fRealTimeRequest = true et
			// si la limie de caractère pour la recherche est atteinte.
			if( text.length == remoteCallStartAt && fRealTimeRequest ){
				remoteGetItemList();
			} 
			
			// Mode déconnecté ou données déjà chargée, on propose la completion
			// si aucun item n'est selectionné
			else if( text.length >= remoteCallStartAt && selectedItem == null ) {
				showCompletion();
			} 
			
			// Sinon on cache
			else {
				hideCompletion();
			}
		}
		
		
		
		/**
		 * Détecteur pour les touches relachées
		 * 
		 * @param e KeyBoardEvent
		 * @author jacksay14
		 */
		protected function handlerKeyUp( e:KeyboardEvent ) :void {
			switch( e.keyCode ) {
				case Keyboard.ESCAPE :
					hideCompletion();
					break;
					
				case Keyboard.DOWN : 
					completionSelectNext();
					break;
					
				case Keyboard.UP : 
					completionSelectPrevious();
					break;
					
				case Keyboard.ENTER :
					completionSelect();
					break;
					
				case Keyboard.BACKSPACE :
					showCompletion();
					break;
			} 
		}
		
		
		
		// ---------------------------------------------------------------------
		//
		// PROTECTED
		//
		// ---------------------------------------------------------------------
		/**
		 * Gestion de l'affichage de la completion
		 * 
		 * @author jacksay14
		 */
		protected function showCompletion() :void {
			
			
			
			// En mode connecté, on ne propose pas la completion si le nombre
			// de caractère saisi est inférieur ) remoteCallStart
			if( text.length <= remoteCallStartAt && fRealTimeRequest ){
				hideCompletion();
				return;
			}
			
			var dtCompletion:ArrayCollection = dataProvider as ArrayCollection;
			
			// Situation anormale
			if( dtCompletion == null ){
				throw new Error("Source de données indisponible");
			}
			
			// actualisation des Filtres/Tries
			dtCompletion.refresh();
			
			
			if( dtCompletion.length > 0 ) { 
				if( completion.visible == false ){
					completion.visible = true;
					PopUpManager.addPopUp(completion, this);
				} else {
					PopUpManager.bringToFront(completion);
				}
				
				// Mise à jour visuel de la completion
				completion.width = width;
				var p:Point = new Point(x, y);
				var globalPosition:Point = localToGlobal(p);
				completion.x = globalPosition.x - x;
				completion.y = globalPosition.y - y + height;
				
				// Ajustement du nombre de ligne affichées
				// Si le nombre d'item est inférieur au MaxRow, on fixe le
				// MaxRow au nombre d'item.
				if( dtCompletion.length < completionMaxRow ){
					completion.rowCount = dtCompletion.length;
				} 
				
				// Sinon on fixe le MaxRow à la valeur par defaut.
				else {
					completion.rowCount = completionMaxRow;
				}
				
			} 
			// Aucune donnée pour la completion (on cache)
			else {
				hideCompletion();
			}
		}
		
		
		
		/**
		 * Cache la completion
		 * 
		 * @author jacksay14
		 */
		protected function hideCompletion() :void {
			if( completion != null && completion.visible ){
				completion.visible = false;
				PopUpManager.removePopUp(completion);
			}	
		}
		
		
		
		/**
		 * Selectionne l'item suivant dans la completion.
		 * 
		 * @author jacksay14
		 */
		protected function completionSelectNext() :void {
			// Si la completion est masquée, on l'affiche
			if( !completion.visible ){
				showCompletion();
			} 
			// Sinon on selectionne le suivant
			else {
				if( completion.selectedIndex >= 0 && completion.selectedIndex < ArrayCollection(completion.dataProvider).length ){
					completion.selectedIndex ++;
				} 
				else {
					completion.selectedIndex = 0;
				}
				completion.scrollToIndex(completion.selectedIndex);
			}
		}
		
		
		
		/**
		 * Selectionne l'item précédent dans la completion
		 * 
		 * @author jacksay14
		 */
		protected function completionSelectPrevious() :void {
			if( completion.selectedIndex > 0 ){
				completion.selectedIndex --;
				completion.scrollToIndex(completion.selectedIndex);
			}
		}
		
		
		
		/**
		 * Valide l'item selectionné dans la completion comme selectedItem
		 * 
		 * @author jacksay14
		 */
		protected function completionSelect() :void {
			if( completion.visible && completion.selectedIndex >= 0 ){
				selectedItem = completion.selectedItem;
				hideCompletion();
			}
		}
		
		
		
		// ---------------------------------------------------------------------
		//
		// REMOTE ACCESS
		//
		// ---------------------------------------------------------------------
		
		/**
		 * <b>Méthode à surcharger</b> pour lancer la requète de 
		 * récupération des listes.
		 * 
		 * @author jacksay14
		 */
		protected function remoteGetItemList() :void {
			throw new AutoCompleterRemoteError(AutoCompleterRemoteError.REMOTE_CALL_LISTE_FUNCTION_UNDEFINED);
		}



		/**
		 * Méthode à executer lors de la récupération des données
		 * 
		 * @author jacksay14
		 */
		protected function remoteGetItemListResult( value:Array ) :void {
			dataProvider = value;
			showCompletion();
		}


		
		/**
		 * <b>Méthode à surcharger</b> pour lancer la requète de récupération 
		 * par ID.
		 * 
		 * @author jacksay14
		 */
		protected function remoteGetItemById( value:int ) :void {
			throw new AutoCompleterRemoteError(AutoCompleterRemoteError.REMOTE_CALL_ITEM_BY_ID_FUNCTION_UNDEFINED);
		}
		
		
		
		/**
		 * Méthode à executer lors de la récupération de la donnée par ID
		 * 
		 * @author jacksay14
		 */
		protected function remoteGetItemByIdResult( item:* ) :void {
			// La donnée semble absente de la base de donnée
			if( item == null ){
				remoteItemNotFound();
			}
			
			// On push l'item obtenu dans le dataProvider
			else 
			{
				ArrayCollection(dataProvider).addItem(item);
				selectedItem = item;
			}
		}
		
		
		
		/**
		 * <b>Méthode à surcharger</b>, elle définit le comportement à adopter 
		 * quand la fonction remoteGetItemByID( value:int ) n'a pas trouvée l'
		 * item via son ID.
		 */
		protected function remoteItemNotFound() :void {
			selectedItem = null;
		}
		
		
		
		// ---------------------------------------------------------------------
		//
		// STYLE
		//
		// ---------------------------------------------------------------------
		
		/**
		 * Rafraichis l'affichage du champ
		 */
		override protected function invalidateStyle():void {
			if( selectedItem == null ){
				super.invalidateStyle();
			} else {
				styleSelected();
			}
		}
		
		
		
		/**
		 * Applique le style quand une donnée est selectionnée
		 */
		protected function styleSelected() :void {
			setStyle("fontWeight","bold");
			setStyle("fontStyle", FontStyle.REGULAR);
			setStyle("color", "none");
		}
		
		
		
		// ---------------------------------------------------------------------
		//
		// OVERRIDE for COMPONENT
		//
		// ---------------------------------------------------------------------
		
		/**
		 * A la perte du focus, on cache la completion
		 * 
		 * @author jacksay14
		 */
		override protected function focusOutHandler(event:FocusEvent):void {
			super.focusOutHandler(event);
			hideCompletion();
		}
		
		/**
		 * Création des enfants
		 * 
		 * @author jacksay14
		 */
		override protected function createChildren():void {
			super.createChildren();
			
			// Création de la liste
			createDropDown();
		}
		
		/** 
		 * Création de la liste permettant de visualiser le jeu de donnée.
		 * 
		 * @author Julien
		 * @author jacksay14 (comment line 01)
		 * @since 2009-12-08
		 */
		private function createDropDown():void {
			completion.focusEnabled = false;
			completion.visible = false;
			completion.labelFunction = setBoldTextFind;
			completion.itemRenderer  = _dropDownItemRenderer ? _dropDownItemRenderer : new ClassFactory( AutoCompleterRemoteCompletionRenderer );
			completion.addEventListener( ListEvent.ITEM_CLICK, handlerItemClick, false, 0, true );
		}
		
		
		override protected function measure():void {
			super.measure();
			measuredWidth	 	= 100;
			measuredMinWidth 	= 50;
			measuredHeight		= 20;
			measuredMinHeight	= 20;
		}
		
		/** 
		 * Filtrage de la complection par defaut
		 *
		protected function completionFilterFunction( o:Object ) :Boolean {
			if( text == "" ) return true;
			
			var pattern:String = "";
			var flag:String = 'g';
			var regExp:RegExp;
			var value:String = (selectedItemLabelFunction==null)?o.toString():selectedItemLabelFunction(o);
			
			if( searchMode == AutoCompleterRemoteMode.BEGIN_BY ) pattern = pattern.concat('^');
			if( !searchSensitiveCase ) flag = flag.concat('i');
			
			pattern = pattern.concat(text);
			regExp = new RegExp(pattern, flag);
			
			return value.match(regExp).length > 0;
			 
		}
		/****/
		
		/** 
		 * Cette fonction permet de mettre en gras le texte 'matché' dans l'
		 * itemRenderer de la completion
		 * Fonction par défaut
		 * 
		 * @author Julien, jacksay14
		 * @since 2009-12-09
		 */
		public function defaultCompletionLabelFunction( item:Object ):String {
			var string:String = selectedItemLabelFunction( item );
			return string;
			var searchStr:String = text;
			
			// there are problems using ">"s and "<"s in HTML
			string = string.replace( "<", "&lt;" ).replace( ">", "&gt;" );				

			var returnStr:String = AutoCompleteStringUtils.highlighMatch( string, searchStr, searchSensitiveCase, searchMode );
			
			returnStr = "<font color='" + colorDropDown + "'>" + returnStr + "</font>";
			
		}
		
		public function setBoldTextFind( item:Object ) :String {
			var string:String = completionLabelFunction( item );
			
			// there are problems using ">"s and "<"s in HTML
			string = string.replace( "<", "&lt;" ).replace( ">", "&gt;" );				

			var returnStr:String = AutoCompleteStringUtils.highlighMatch( string, text, searchSensitiveCase, searchMode );
			
			returnStr = "<font color='" + colorDropDown + "'>" + returnStr + "</font>";
			
			return returnStr;
			
		}
		
		
		
		/** 
		 * Cette fonction permet de formatter chaque élément de la liste
		 * Fonction par défaut
		 * 
		 * @author Julien, jacksay14
		 * @since 2009-12-09
		 */
		private function defaultSelectedItemLabelFunction( item:Object ):String {
			if (labelField && item.hasOwnProperty( labelField )) {
				return item[ labelField ];
			} else {
				throw new Error("Vous devez spécifier une selectedItemLabelFunction(o:*) ou renseigner la propriété 'labelField' pour l'affichage du texte")
			}
		}
		
		
		
		/** 
		 * Cette fonction permet de filter le jeu de données.
		 * Fonction par défaut
		 * 
		 * @author Julien
		 * @since 2009-12-09
		 */
		public function defaultFilterFunction( item:Object ):Boolean {
			if (text.length == 0)
			{
				return true;
			}
			
			var str:String = selectedItemLabelFunction( item );
			
			switch (searchMode)
			{
				case AutoCompleterRemoteMode.CONTAIN:
					return AutoCompleteStringUtils.contains( str, text );
				case AutoCompleterRemoteMode.BEGIN_BY:
					return AutoCompleteStringUtils.beginsWith( str, text );
				case AutoCompleterRemoteMode.WORD:
					return AutoCompleteStringUtils.anyWordBeginsWith( str, text );
			}
			
			return false;
		}
		
		
		
		/** 
		 * Force le rafraîchissement de la liste
		 * 
		 * @author Julien
		 * @since 2009-12-09
		 */
		public function filterData():void {
			ICollectionView(dataProvider).filterFunction = filterFunction;
			ICollectionView(dataProvider).refresh();
		}
	}
}